//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.amazonaws.services.s3.transfer;

import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.AmazonWebServiceRequest;
import com.amazonaws.annotation.SdkInternalApi;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
import com.amazonaws.event.ProgressListener;
import com.amazonaws.event.ProgressListenerChain;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.internal.FileLocks;
import com.amazonaws.services.s3.internal.Mimetypes;
import com.amazonaws.services.s3.internal.RequestCopyUtils;
import com.amazonaws.services.s3.internal.ServiceUtils;
import com.amazonaws.services.s3.model.*;
import com.amazonaws.services.s3.transfer.Transfer.TransferState;
import com.amazonaws.services.s3.transfer.exception.FileLockException;
import com.amazonaws.services.s3.transfer.internal.*;
import com.amazonaws.util.VersionInfoUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class MyTransferManager extends TransferManager {
    private final AmazonS3 s3;
    private TransferManagerConfiguration configuration;
    private final ExecutorService executorService;
    private final ScheduledExecutorService timedThreadPool;
    private static final Logger log = LoggerFactory.getLogger(TransferManager.class);
    private final boolean shutDownThreadPools;
    private final boolean isImmutable;
    private static final String USER_AGENT = TransferManager.class.getName() + "/" + VersionInfoUtils.getVersion();
    private static final String USER_AGENT_MULTIPART = TransferManager.class.getName() + "_multipart/" + VersionInfoUtils.getVersion();
    private static final String DEFAULT_DELIMITER = "/";
    private static final ThreadFactory daemonThreadFactory = new ThreadFactory() {
        final AtomicInteger threadCount = new AtomicInteger(0);

        public Thread newThread(Runnable r) {
            int threadNumber = this.threadCount.incrementAndGet();
            Thread thread = new Thread(r);
            thread.setDaemon(true);
            thread.setName("S3TransferManagerTimedThread-" + threadNumber);
            return thread;
        }
    };

    /** @deprecated */
    @Deprecated
    public MyTransferManager() {
        this((AmazonS3)(new AmazonS3Client(new DefaultAWSCredentialsProviderChain())));
    }

    /** @deprecated */
    @Deprecated
    public MyTransferManager(AWSCredentialsProvider credentialsProvider) {
        this((AmazonS3)(new AmazonS3Client(credentialsProvider)));
    }

    /** @deprecated */
    @Deprecated
    public MyTransferManager(AWSCredentials credentials) {
        this((AmazonS3)(new AmazonS3Client(credentials)));
    }

    /** @deprecated */
    @Deprecated
    public MyTransferManager(AmazonS3 s3) {
        this(s3, TransferManagerUtils.createDefaultExecutorService());
    }

    /** @deprecated */
    @Deprecated
    public MyTransferManager(AmazonS3 s3, ExecutorService executorService) {
        this(s3, executorService, true);
    }

    /** @deprecated */
    @Deprecated
    public MyTransferManager(AmazonS3 s3, ExecutorService executorService, boolean shutDownThreadPools) {
        this.timedThreadPool = new ScheduledThreadPoolExecutor(1, daemonThreadFactory);
        this.s3 = s3;
        this.executorService = executorService;
        this.configuration = new TransferManagerConfiguration();
        this.shutDownThreadPools = shutDownThreadPools;
        this.isImmutable = false;
    }

    @SdkInternalApi
    MyTransferManager(TransferManagerParams params) {
        this.timedThreadPool = new ScheduledThreadPoolExecutor(1, daemonThreadFactory);
        this.s3 = params.getS3Client();
        this.executorService = params.getExecutorService();
        this.configuration = params.getConfiguration();
        this.shutDownThreadPools = params.getShutDownThreadPools();
        this.isImmutable = true;
    }

    protected MyTransferManager(TransferManagerBuilder builder) {
        this(builder.getParams());
    }

    /** @deprecated */
    @Deprecated
    public void setConfiguration(TransferManagerConfiguration configuration) {
        this.checkMutability();
        this.configuration = configuration;
    }

    public TransferManagerConfiguration getConfiguration() {
        return this.configuration;
    }

    public AmazonS3 getAmazonS3Client() {
        return this.s3;
    }

    public MyUploadImpl upload(String bucketName, String key, InputStream input, ObjectMetadata objectMetadata) throws AmazonServiceException, AmazonClientException {
        return this.upload(new PutObjectRequest(bucketName, key, input, objectMetadata));
    }

    public MyUploadImpl upload(String bucketName, String key, File file) throws AmazonServiceException, AmazonClientException {
        return this.upload(new PutObjectRequest(bucketName, key, file));
    }

    public MyUploadImpl upload(PutObjectRequest putObjectRequest) throws AmazonServiceException, AmazonClientException {
        return this.doUpload(putObjectRequest, (TransferStateChangeListener)null, (S3ProgressListener)null, (PersistableUpload)null);
    }

    public MyUploadImpl upload(PutObjectRequest putObjectRequest, S3ProgressListener progressListener) throws AmazonServiceException, AmazonClientException {
        return this.doUpload(putObjectRequest, (TransferStateChangeListener)null, progressListener, (PersistableUpload)null);
    }

    private MyUploadImpl doUpload(PutObjectRequest putObjectRequest, TransferStateChangeListener stateListener, S3ProgressListener progressListener, PersistableUpload persistableUpload) throws AmazonServiceException, AmazonClientException {
        appendSingleObjectUserAgent(putObjectRequest);
        String multipartUploadId = persistableUpload != null ? persistableUpload.getMultipartUploadId() : null;
        if (putObjectRequest.getMetadata() == null) {
            putObjectRequest.setMetadata(new ObjectMetadata());
        }

        ObjectMetadata metadata = putObjectRequest.getMetadata();
        File file = TransferManagerUtils.getRequestFile(putObjectRequest);
        if (file != null) {
            metadata.setContentLength(file.length());
            if (metadata.getContentType() == null) {
                metadata.setContentType(Mimetypes.getInstance().getMimetype(file));
            }
        } else if (multipartUploadId != null) {
            throw new IllegalArgumentException("Unable to resume the upload. No file specified.");
        }

        String description = "Uploading to " + putObjectRequest.getBucketName() + "/" + putObjectRequest.getKey();
        TransferProgress transferProgress = new TransferProgress();
        transferProgress.setTotalBytesToTransfer(TransferManagerUtils.getContentLength(putObjectRequest));
        S3ProgressListenerChain listenerChain = new S3ProgressListenerChain(new ProgressListener[]{new TransferProgressUpdatingListener(transferProgress), putObjectRequest.getGeneralProgressListener(), progressListener});
        putObjectRequest.setGeneralProgressListener(listenerChain);
        MyUploadImpl upload = new MyUploadImpl(putObjectRequest, description, transferProgress, listenerChain, stateListener);
        UploadCallable uploadCallable = new UploadCallable(this, this.executorService, upload, putObjectRequest, listenerChain, multipartUploadId, transferProgress);
        UploadMonitor watcher = UploadMonitor.create(this, upload, this.executorService, uploadCallable, putObjectRequest, listenerChain);
        upload.setMonitor(watcher);
        return upload;
    }

    public Download download(String bucket, String key, File file) {
        return this.download(bucket, key, file, 0L);
    }

    public Download download(String bucket, String key, File file, long timeoutMillis) {
        return this.download(new GetObjectRequest(bucket, key), file, timeoutMillis);
    }

    public Download download(GetObjectRequest getObjectRequest, File file) {
        return this.download(getObjectRequest, file, 0L);
    }

    public Download download(GetObjectRequest getObjectRequest, File file, long timeoutMillis) {
        return this.doDownload(getObjectRequest, file, (TransferStateChangeListener)null, (S3ProgressListener)null, false, timeoutMillis, (Integer)null, 0L);
    }

    public Download download(GetObjectRequest getObjectRequest, File file, S3ProgressListener progressListener) {
        return this.doDownload(getObjectRequest, file, (TransferStateChangeListener)null, progressListener, false, 0L, (Integer)null, 0L);
    }

    public Download download(GetObjectRequest getObjectRequest, File file, S3ProgressListener progressListener, long timeoutMillis) {
        return this.doDownload(getObjectRequest, file, (TransferStateChangeListener)null, progressListener, false, timeoutMillis, (Integer)null, 0L);
    }

    public Download download(GetObjectRequest getObjectRequest, File file, S3ProgressListener progressListener, long timeoutMillis, boolean resumeOnRetry) {
        return this.doDownload(getObjectRequest, file, (TransferStateChangeListener)null, progressListener, false, timeoutMillis, (Integer)null, 0L, resumeOnRetry);
    }

    private Download doDownload(GetObjectRequest getObjectRequest, File file, TransferStateChangeListener stateListener, S3ProgressListener s3progressListener, boolean resumeExistingDownload, long timeoutMillis, Integer lastFullyDownloadedPart, long lastModifiedTimeRecordedDuringPause) {
        return this.doDownload(getObjectRequest, file, stateListener, s3progressListener, resumeExistingDownload, timeoutMillis, lastFullyDownloadedPart, lastModifiedTimeRecordedDuringPause, false);
    }

    private Download doDownload(GetObjectRequest getObjectRequest, File file, TransferStateChangeListener stateListener, S3ProgressListener s3progressListener, boolean resumeExistingDownload, long timeoutMillis, Integer lastFullyDownloadedPart, long lastModifiedTimeRecordedDuringPause, boolean resumeOnRetry) {
        this.assertParameterNotNull(getObjectRequest, "A valid GetObjectRequest must be provided to initiate download");
        this.assertParameterNotNull(file, "A valid file must be provided to download into");
        appendSingleObjectUserAgent(getObjectRequest);
        String description = "Downloading from " + getObjectRequest.getBucketName() + "/" + getObjectRequest.getKey();
        TransferProgress transferProgress = new TransferProgress();
        S3ProgressListenerChain listenerChain = new S3ProgressListenerChain(new ProgressListener[]{new TransferProgressUpdatingListener(transferProgress), getObjectRequest.getGeneralProgressListener(), s3progressListener});
        getObjectRequest.setGeneralProgressListener(new ProgressListenerChain(new TransferCompletionFilter(), new ProgressListener[]{listenerChain}));
        GetObjectMetadataRequest getObjectMetadataRequest = RequestCopyUtils.createGetObjectMetadataRequestFrom(getObjectRequest);
        ObjectMetadata objectMetadata = this.s3.getObjectMetadata(getObjectMetadataRequest);
        long lastModifiedTime = objectMetadata.getLastModified().getTime();
        long startingByte = 0L;
        long[] range = getObjectRequest.getRange();
        long lastByte;
        if (range != null && range.length == 2) {
            startingByte = range[0];
            lastByte = range[1];
        } else {
            lastByte = objectMetadata.getContentLength() - 1L;
        }

        long origStartingByte = startingByte;
        boolean isDownloadParallel = !this.configuration.isDisableParallelDownloads() && TransferManagerUtils.isDownloadParallelizable(this.s3, getObjectRequest, ServiceUtils.getPartCount(getObjectRequest, this.s3));
        DownloadImpl download = new DownloadImpl(description, transferProgress, listenerChain, (S3Object)null, stateListener, getObjectRequest, file, objectMetadata, isDownloadParallel);
        long totalBytesToDownload = lastByte - startingByte + 1L;
        transferProgress.setTotalBytesToTransfer(totalBytesToDownload);
        if (totalBytesToDownload > 0L && !isDownloadParallel) {
            getObjectRequest.withRange(startingByte, lastByte);
        }

        long fileLength = -1L;
        if (resumeExistingDownload) {
            if (this.isS3ObjectModifiedSincePause(lastModifiedTime, lastModifiedTimeRecordedDuringPause)) {
                throw new AmazonClientException("The requested object in bucket " + getObjectRequest.getBucketName() + " with key " + getObjectRequest.getKey() + " is modified on Amazon S3 since the last pause.");
            }

            getObjectRequest.setUnmodifiedSinceConstraint(new Date(lastModifiedTime));
            if (!isDownloadParallel) {
                if (!FileLocks.lock(file)) {
                    throw new FileLockException("Fail to lock " + file + " for resume download");
                }

                try {
                    if (file.exists()) {
                        fileLength = file.length();
                        startingByte += fileLength;
                        getObjectRequest.setRange(startingByte, lastByte);
                        transferProgress.updateProgress(Math.min(fileLength, totalBytesToDownload));
                        totalBytesToDownload = lastByte - startingByte + 1L;
                        if (log.isDebugEnabled()) {
                            log.debug("Resume download: totalBytesToDownload=" + totalBytesToDownload + ", origStartingByte=" + origStartingByte + ", startingByte=" + startingByte + ", lastByte=" + lastByte + ", numberOfBytesRead=" + fileLength + ", file: " + file);
                        }
                    }
                } finally {
                    FileLocks.unlock(file);
                }
            }
        }

        if (totalBytesToDownload < 0L) {
            throw new IllegalArgumentException("Unable to determine the range for download operation.");
        } else {
            CountDownLatch latch = new CountDownLatch(1);
            Future<File> future = this.executorService.submit(new DownloadCallable(this.s3, latch, getObjectRequest, resumeExistingDownload, download, file, startingByte, fileLength, timeoutMillis, this.timedThreadPool, this.executorService, lastFullyDownloadedPart, isDownloadParallel, resumeOnRetry));
            download.setMonitor(new DownloadMonitor(download, future));
            latch.countDown();
            return download;
        }
    }

    private boolean isS3ObjectModifiedSincePause(long lastModifiedTimeRecordedDuringResume, long lastModifiedTimeRecordedDuringPause) {
        return lastModifiedTimeRecordedDuringResume != lastModifiedTimeRecordedDuringPause;
    }

    public MultipleFileDownload downloadDirectory(String bucketName, String keyPrefix, File destinationDirectory) {
        return this.downloadDirectory(bucketName, keyPrefix, destinationDirectory, false);
    }

    public MultipleFileDownload downloadDirectory(String bucketName, String keyPrefix, File destinationDirectory, KeyFilter filter) {
        return this.downloadDirectory(bucketName, keyPrefix, destinationDirectory, false, filter);
    }

    public MultipleFileDownload downloadDirectory(String bucketName, String keyPrefix, File destinationDirectory, boolean resumeOnRetry) {
        return this.downloadDirectory(bucketName, keyPrefix, destinationDirectory, resumeOnRetry, (KeyFilter)null);
    }

    public MultipleFileDownload downloadDirectory(String bucketName, String keyPrefix, File destinationDirectory, boolean resumeOnRetry, KeyFilter filter) {
        if (keyPrefix == null) {
            keyPrefix = "";
        }

        if (filter == null) {
            filter = KeyFilter.INCLUDE_ALL;
        }

        List<S3ObjectSummary> objectSummaries = new LinkedList();
        Stack<String> commonPrefixes = new Stack();
        commonPrefixes.add(keyPrefix);
        long totalSize = 0L;

        do {
            String prefix = (String)commonPrefixes.pop();
            ObjectListing listObjectsResponse = null;

            do {
                if (listObjectsResponse == null) {
                    ListObjectsRequest listObjectsRequest = (new ListObjectsRequest()).withBucketName(bucketName).withDelimiter("/").withPrefix(prefix);
                    listObjectsResponse = this.s3.listObjects(listObjectsRequest);
                } else {
                    listObjectsResponse = this.s3.listNextBatchOfObjects(listObjectsResponse);
                }

                Iterator var24 = listObjectsResponse.getObjectSummaries().iterator();

                while(true) {
                    while(var24.hasNext()) {
                        S3ObjectSummary s = (S3ObjectSummary)var24.next();
                        if (!filter.shouldInclude(s)) {
                            log.debug("Skipping " + s.getKey() + " as it does not match filter.");
                        } else {
                            if (this.leavesRoot(destinationDirectory, s.getKey())) {
                                throw new RuntimeException("Cannot download key " + s.getKey() + ", its relative path resolves outside the parent directory.");
                            }

                            if (!s.getKey().equals(prefix) && !listObjectsResponse.getCommonPrefixes().contains(s.getKey() + "/")) {
                                objectSummaries.add(s);
                                totalSize += s.getSize();
                            } else {
                                log.debug("Skipping download for object " + s.getKey() + " since it is also a virtual directory");
                            }
                        }
                    }

                    commonPrefixes.addAll(listObjectsResponse.getCommonPrefixes());
                    break;
                }
            } while(listObjectsResponse.isTruncated());
        } while(!commonPrefixes.isEmpty());

        ProgressListenerChain additionalListeners = new ProgressListenerChain(new ProgressListener[0]);
        TransferProgress transferProgress = new TransferProgress();
        transferProgress.setTotalBytesToTransfer(totalSize);
        ProgressListener listener = new MultipleFileTransferProgressUpdatingListener(transferProgress, additionalListeners);
        List<DownloadImpl> downloads = new ArrayList();
        String description = "Downloading from " + bucketName + "/" + keyPrefix;
        MultipleFileDownloadImpl multipleFileDownload = new MultipleFileDownloadImpl(description, transferProgress, additionalListeners, keyPrefix, bucketName, downloads);
        multipleFileDownload.setMonitor(new MultipleFileTransferMonitor(multipleFileDownload, downloads));
        CountDownLatch latch = new CountDownLatch(1);
        MultipleFileTransferStateChangeListener transferListener = new MultipleFileTransferStateChangeListener(latch, multipleFileDownload);
        Iterator var18 = objectSummaries.iterator();

        while(var18.hasNext()) {
            S3ObjectSummary summary = (S3ObjectSummary)var18.next();
            File f = new File(destinationDirectory, summary.getKey());
            File parentFile = f.getParentFile();
            if (!parentFile.exists() && !parentFile.mkdirs()) {
                throw new RuntimeException("Couldn't create parent directories for " + f.getAbsolutePath());
            }

            GetObjectRequest req = (GetObjectRequest)(new GetObjectRequest(summary.getBucketName(), summary.getKey())).withGeneralProgressListener(listener);
            downloads.add((DownloadImpl)this.doDownload(req, f, transferListener, (S3ProgressListener)null, false, 0L, (Integer)null, 0L, resumeOnRetry));
        }

        if (downloads.isEmpty()) {
            multipleFileDownload.setState(TransferState.Completed);
            return multipleFileDownload;
        } else {
            latch.countDown();
            return multipleFileDownload;
        }
    }

    private boolean leavesRoot(File localBaseDirectory, String key) {
        try {
            return !(new File(localBaseDirectory, key)).getCanonicalPath().startsWith(localBaseDirectory.getCanonicalPath());
        } catch (IOException var4) {
            throw new RuntimeException("Unable to canonicalize paths", var4);
        }
    }

    public MultipleFileUpload uploadDirectory(String bucketName, String virtualDirectoryKeyPrefix, File directory, boolean includeSubdirectories) {
        return this.uploadDirectory(bucketName, virtualDirectoryKeyPrefix, directory, includeSubdirectories, (ObjectMetadataProvider)null);
    }

    public MultipleFileUpload uploadDirectory(String bucketName, String virtualDirectoryKeyPrefix, File directory, boolean includeSubdirectories, ObjectMetadataProvider metadataProvider) {
        return this.uploadDirectory(bucketName, virtualDirectoryKeyPrefix, directory, includeSubdirectories, metadataProvider, (ObjectTaggingProvider)null);
    }

    public MultipleFileUpload uploadDirectory(String bucketName, String virtualDirectoryKeyPrefix, File directory, boolean includeSubdirectories, ObjectMetadataProvider metadataProvider, ObjectTaggingProvider taggingProvider) {
        if (directory != null && directory.exists() && directory.isDirectory()) {
            List<File> files = new LinkedList();
            this.listFiles(directory, files, includeSubdirectories);
            return this.uploadFileList(bucketName, virtualDirectoryKeyPrefix, directory, files, metadataProvider, taggingProvider);
        } else {
            throw new IllegalArgumentException("Must provide a directory to upload");
        }
    }

    public MultipleFileUpload uploadFileList(String bucketName, String virtualDirectoryKeyPrefix, File directory, List<File> files) {
        return this.uploadFileList(bucketName, virtualDirectoryKeyPrefix, directory, files, (ObjectMetadataProvider)null);
    }

//    public MultipleFileUpload uploadFileList(String bucketName, String virtualDirectoryKeyPrefix, File directory, List<File> files, ObjectMetadataProvider metadataProvider) {
//        return this.uploadFileList(bucketName, virtualDirectoryKeyPrefix, directory, files, metadataProvider, (ObjectTaggingProvider)null, null);
//    }

    public MultipleFileUpload uploadFileList(String bucketName, String virtualDirectoryKeyPrefix, File directory, List<File> files, ObjectMetadataProvider metadataProvider, CannedAccessControlList cannedAccessControlList) {
        return this.uploadFileList(bucketName, virtualDirectoryKeyPrefix, directory, files, metadataProvider, (ObjectTaggingProvider)null, cannedAccessControlList);
    }

    public MultipleFileUpload uploadFileList(String bucketName, String virtualDirectoryKeyPrefix, File directory, List<File> files, ObjectMetadataProvider metadataProvider, ObjectTaggingProvider taggingProvider, CannedAccessControlList cannedAccessControlList) {
        if (directory != null && directory.exists() && directory.isDirectory()) {
            if (virtualDirectoryKeyPrefix != null && virtualDirectoryKeyPrefix.length() != 0) {
                if (!virtualDirectoryKeyPrefix.endsWith("/")) {
                    virtualDirectoryKeyPrefix = virtualDirectoryKeyPrefix + "/";
                }
            } else {
                virtualDirectoryKeyPrefix = "";
            }

            ProgressListenerChain additionalListeners = new ProgressListenerChain(new ProgressListener[0]);
            TransferProgress progress = new TransferProgress();
            ProgressListener listener = new MultipleFileTransferProgressUpdatingListener(progress, additionalListeners);
            List<UploadImpl> uploads = new LinkedList();
            MultipleFileUploadImpl multipleFileUpload = new MultipleFileUploadImpl("Uploading etc", progress, additionalListeners, virtualDirectoryKeyPrefix, bucketName, uploads);
            multipleFileUpload.setMonitor(new MultipleFileTransferMonitor(multipleFileUpload, uploads));
            CountDownLatch latch = new CountDownLatch(1);
            MultipleFileTransferStateChangeListener transferListener = new MultipleFileTransferStateChangeListener(latch, multipleFileUpload);
            if (files != null && !files.isEmpty()) {
                int startingPosition = directory.getAbsolutePath().length();
                if (!directory.getAbsolutePath().endsWith(File.separator)) {
                    ++startingPosition;
                }

                long totalSize = 0L;
                Iterator var17 = files.iterator();

                while(var17.hasNext()) {
                    File f = (File)var17.next();
                    if (f.isFile()) {
                        totalSize += f.length();
                        String key = f.getAbsolutePath().substring(startingPosition).replaceAll("\\\\", "/");
                        UploadContext uploadContext = new UploadContext(f, bucketName, key);
                        ObjectMetadata metadata = new ObjectMetadata();
                        ObjectTagging objectTagging = null;
                        if (metadataProvider != null) {
                            metadataProvider.provideObjectMetadata(f, metadata);
                        }

                        if (taggingProvider != null) {
                            objectTagging = taggingProvider.provideObjectTags(uploadContext);
                        }
                        uploads.add(this.doUpload(
                                (new PutObjectRequest(
                                        bucketName,
                                        virtualDirectoryKeyPrefix + key, f)).withMetadata(metadata)
                                            .withTagging(objectTagging)
                                            .withCannedAcl(cannedAccessControlList)
                                            .withGeneralProgressListener(listener),
                                transferListener,
                                (S3ProgressListener)null,
                                (PersistableUpload)null));
                    }
                }

                progress.setTotalBytesToTransfer(totalSize);
            } else {
                multipleFileUpload.setState(TransferState.Completed);
            }

            latch.countDown();
            return multipleFileUpload;
        } else {
            throw new IllegalArgumentException("Must provide a common base directory for uploaded files");
        }
    }

    private void listFiles(File dir, List<File> results, boolean includeSubDirectories) {
        File[] found = dir.listFiles();
        if (found != null) {
            File[] var5 = found;
            int var6 = found.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                File f = var5[var7];
                if (f.isDirectory()) {
                    if (includeSubDirectories) {
                        this.listFiles(f, results, includeSubDirectories);
                    }
                } else {
                    results.add(f);
                }
            }
        }

    }

    public void abortMultipartUploads(String bucketName, Date date) throws AmazonServiceException, AmazonClientException {
        MultipartUploadListing uploadListing = this.s3.listMultipartUploads((ListMultipartUploadsRequest)appendSingleObjectUserAgent(new ListMultipartUploadsRequest(bucketName)));

        do {
            Iterator var4 = uploadListing.getMultipartUploads().iterator();

            while(var4.hasNext()) {
                MultipartUpload upload = (MultipartUpload)var4.next();
                if (upload.getInitiated().compareTo(date) < 0) {
                    this.s3.abortMultipartUpload((AbortMultipartUploadRequest)appendSingleObjectUserAgent(new AbortMultipartUploadRequest(bucketName, upload.getKey(), upload.getUploadId())));
                }
            }

            ListMultipartUploadsRequest request = (new ListMultipartUploadsRequest(bucketName)).withUploadIdMarker(uploadListing.getNextUploadIdMarker()).withKeyMarker(uploadListing.getNextKeyMarker());
            uploadListing = this.s3.listMultipartUploads((ListMultipartUploadsRequest)appendSingleObjectUserAgent(request));
        } while(uploadListing.isTruncated());

    }

    public void shutdownNow() {
        this.shutdownNow(true);
    }

    public void shutdownNow(boolean shutDownS3Client) {
        if (this.shutDownThreadPools) {
            this.executorService.shutdownNow();
            this.timedThreadPool.shutdownNow();
        }

        if (shutDownS3Client) {
            this.s3.shutdown();
        }

    }

    private void shutdownThreadPools() {
        if (this.shutDownThreadPools) {
            this.executorService.shutdown();
            this.timedThreadPool.shutdown();
        }

    }

    public static <X extends AmazonWebServiceRequest> X appendSingleObjectUserAgent(X request) {
        request.getRequestClientOptions().appendUserAgent(USER_AGENT);
        return request;
    }

    public static <X extends AmazonWebServiceRequest> X appendMultipartUserAgent(X request) {
        request.getRequestClientOptions().appendUserAgent(USER_AGENT_MULTIPART);
        return request;
    }

    public Copy copy(String sourceBucketName, String sourceKey, String destinationBucketName, String destinationKey) throws AmazonServiceException, AmazonClientException {
        return this.copy(new CopyObjectRequest(sourceBucketName, sourceKey, destinationBucketName, destinationKey));
    }

    public Copy copy(CopyObjectRequest copyObjectRequest) {
        return this.copy(copyObjectRequest, (TransferStateChangeListener)null);
    }

    public Copy copy(CopyObjectRequest copyObjectRequest, TransferStateChangeListener stateChangeListener) throws AmazonServiceException, AmazonClientException {
        return this.copy(copyObjectRequest, this.s3, stateChangeListener);
    }

    public Copy copy(CopyObjectRequest copyObjectRequest, AmazonS3 srcS3, TransferStateChangeListener stateChangeListener) throws AmazonServiceException, AmazonClientException {
        appendSingleObjectUserAgent(copyObjectRequest);
        this.assertParameterNotNull(copyObjectRequest.getSourceBucketName(), "The source bucket name must be specified when a copy request is initiated.");
        this.assertParameterNotNull(copyObjectRequest.getSourceKey(), "The source object key must be specified when a copy request is initiated.");
        this.assertParameterNotNull(copyObjectRequest.getDestinationBucketName(), "The destination bucket name must be specified when a copy request is initiated.");
        this.assertParameterNotNull(copyObjectRequest.getDestinationKey(), "The destination object key must be specified when a copy request is initiated.");
        this.assertParameterNotNull(srcS3, "The srcS3 parameter is mandatory");
        String description = "Copying object from " + copyObjectRequest.getSourceBucketName() + "/" + copyObjectRequest.getSourceKey() + " to " + copyObjectRequest.getDestinationBucketName() + "/" + copyObjectRequest.getDestinationKey();
        GetObjectMetadataRequest getObjectMetadataRequest = (new GetObjectMetadataRequest(copyObjectRequest.getSourceBucketName(), copyObjectRequest.getSourceKey())).withSSECustomerKey(copyObjectRequest.getSourceSSECustomerKey()).withRequesterPays(copyObjectRequest.isRequesterPays()).withVersionId(copyObjectRequest.getSourceVersionId());
        ObjectMetadata metadata = srcS3.getObjectMetadata(getObjectMetadataRequest);
        TransferProgress transferProgress = new TransferProgress();
        transferProgress.setTotalBytesToTransfer(metadata.getContentLength());
        ProgressListenerChain listenerChain = new ProgressListenerChain(new ProgressListener[]{new TransferProgressUpdatingListener(transferProgress)});
        CopyImpl copy = new CopyImpl(description, transferProgress, listenerChain, stateChangeListener);
        CopyCallable copyCallable = new CopyCallable(this, this.executorService, copy, copyObjectRequest, metadata, listenerChain);
        CopyMonitor watcher = CopyMonitor.create(this, copy, this.executorService, copyCallable, copyObjectRequest, listenerChain);
        copy.setMonitor(watcher);
        return copy;
    }

    public Upload resumeUpload(PersistableUpload persistableUpload) {
        this.assertParameterNotNull(persistableUpload, "PauseUpload is mandatory to resume a upload.");
        this.configuration.setMinimumUploadPartSize(persistableUpload.getPartSize());
        this.configuration.setMultipartUploadThreshold(persistableUpload.getMutlipartUploadThreshold());
        return this.doUpload(new PutObjectRequest(persistableUpload.getBucketName(), persistableUpload.getKey(), new File(persistableUpload.getFile())), (TransferStateChangeListener)null, (S3ProgressListener)null, persistableUpload);
    }

    public Download resumeDownload(PersistableDownload persistableDownload) {
        this.assertParameterNotNull(persistableDownload, "PausedDownload is mandatory to resume a download.");
        GetObjectRequest request = new GetObjectRequest(persistableDownload.getBucketName(), persistableDownload.getKey(), persistableDownload.getVersionId());
        if (persistableDownload.getRange() != null && persistableDownload.getRange().length == 2) {
            long[] range = persistableDownload.getRange();
            request.setRange(range[0], range[1]);
        }

        request.setRequesterPays(persistableDownload.isRequesterPays());
        request.setResponseHeaders(persistableDownload.getResponseHeaders());
        return this.doDownload(request, new File(persistableDownload.getFile()), (TransferStateChangeListener)null, (S3ProgressListener)null, true, 0L, persistableDownload.getLastFullyDownloadedPartNumber(), persistableDownload.getlastModifiedTime());
    }

    private void assertParameterNotNull(Object parameterValue, String errorMessage) {
        if (parameterValue == null) {
            throw new IllegalArgumentException(errorMessage);
        }
    }

    protected void finalize() throws Throwable {
        this.shutdownThreadPools();
    }

    private void checkMutability() {
        if (this.isImmutable) {
            throw new UnsupportedOperationException("TransferManager is immutable when created with the builder.");
        }
    }
}
